﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
using Notoric.Gui.Windows;

namespace Notoric.Gui.Commands
{
    /// <summary>
    /// A common base class for commands that manipulate a selection
    /// of the <see cref="ViewModel.Elements.CWNotation.MusicSectionInterior"/>.
    /// </summary>
    public abstract class MusicSelectionRibbonCommand : RibbonCommand
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MusicSelectionRibbonCommand"/> class.
        /// </summary>
        /// <param name="mainWindow">
        /// <para>
        /// Type: <see cref="MainWindow"/>
        /// </para>
        /// <para>
        /// The main application window.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="mainWindow"/> is <see langword="null"/>.
        /// </exception>
        protected MusicSelectionRibbonCommand(MainWindow mainWindow)
            : base(mainWindow)
        {
            mainWindow.DocumentPane.SelectionChanged += new SelectionChangedEventHandler(DocumentPane_SelectionChanged);
        }

        #endregion

        #region ICommand Members

        /// <inheritdoc/>
        public override bool CanExecute(object parameter)
        {
            return Selection != null && Selection.Count > 0 && CanExecuteCore(parameter);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when a property of the main application window has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void DocumentPane_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (activePage != null)
            {
                activePage.SelectedSections.CollectionChanged -= SelectedSections_CollectionChanged;

                foreach (var section in activePage.SelectedSections)
                    section.PropertyChanged -= section_PropertyChanged;

                Selection = null;
            }
            else if (activeLinearView != null)
                activeLinearView.Content.PropertyChanged -= section_PropertyChanged;

            activePage = ApplicationWindow.ActivePage;
            activeLinearView = null;

            if (activePage != null)
            {
                activePage.SelectedSections.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedSections_CollectionChanged);

                foreach (var section in activePage.SelectedSections)
                {
                    if (section.Interior is ViewModel.Elements.CWNotation.MusicSectionInterior)
                    {
                        section.PropertyChanged += new PropertyChangedEventHandler(section_PropertyChanged);

                        if (section.IsContentSelected)
                            Selection = ((ViewModel.Elements.CWNotation.MusicSectionInterior)section.Interior).Selection;
                    }
                }
            }
            else if (ApplicationWindow.DocumentPane.SelectedItem is LinearCompositionViewPanel)
            {
                activeLinearView = (ApplicationWindow.DocumentPane.SelectedItem as LinearCompositionViewPanel).DataContext as ViewModel.Elements.LinearCompositionView;
                var section = activeLinearView.Content;
                if (section.Interior is ViewModel.Elements.CWNotation.MusicSectionInterior)
                {
                    section.PropertyChanged += new PropertyChangedEventHandler(section_PropertyChanged);

                    if (section.IsContentSelected)
                        Selection = ((ViewModel.Elements.CWNotation.MusicSectionInterior)section.Interior).Selection;
                }
            }
        }

        /// <summary>
        /// Occurs when a property of a selected music section has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void section_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsContentSelected")
            {
                var section = (ViewModel.Elements.Section)sender;
                if (section.IsContentSelected)
                {
                    var interior = (ViewModel.Elements.CWNotation.MusicSectionInterior)section.Interior;
                    Selection = interior.Selection;
                }
                RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// Occurs when the active selection has changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Selection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Occurs when the selected sections on the active page have changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        void SelectedSections_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (ViewModel.Elements.Section section in e.NewItems)
                    {
                        if (section.Interior is ViewModel.Elements.CWNotation.MusicSectionInterior)
                            section.PropertyChanged += new PropertyChangedEventHandler(section_PropertyChanged);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (ViewModel.Elements.Section section in e.OldItems)
                        section.PropertyChanged -= section_PropertyChanged;
                    break;
                case NotifyCollectionChangedAction.Reset:
                    foreach (var section in activePage.Sections)
                        section.PropertyChanged -= section_PropertyChanged;
                    break;
            }

            var activeSection = activePage.Sections.FirstOrDefault(s => s.IsContentSelected);
            if (activeSection != null && activeSection.Interior is ViewModel.Elements.CWNotation.MusicSectionInterior)
                Selection = ((ViewModel.Elements.CWNotation.MusicSectionInterior)activeSection.Interior).Selection;
        }        

        #endregion

        #region Protected Methods

        /// <summary>
        /// When overriden in a derived class, this method indicates whether
        /// the command can be executed or not.
        /// </summary>
        /// <param name="parameter">Execution parameter</param>
        /// <returns>
        /// <see langword="true"/> if the command can execute; <see lagnword="false"/> otherwise.
        /// </returns>
        protected abstract bool CanExecuteCore(object parameter);

        #endregion

        #region Protected Properties

        /// <summary>
        /// Gets the current music selection.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ReadOnlyObservableCollection{T}"/> of <see cref="ViewModel.Elements.CWNotation.CoreSymbol"/>
        /// </para>
        /// <para>
        /// The current music selection.
        /// </para>
        /// </value>
        protected ReadOnlyObservableCollection<ViewModel.Elements.CWNotation.CoreSymbol> Selection
        {
            get { return selection; }
            private set
            {
                if (selection != null)
                    ((INotifyCollectionChanged)selection).CollectionChanged -= Selection_CollectionChanged;

                selection = value;

                if (selection != null)
                    ((INotifyCollectionChanged)selection).CollectionChanged += new NotifyCollectionChangedEventHandler(Selection_CollectionChanged);
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the active linear composition view.
        /// </summary>
        private ViewModel.Elements.LinearCompositionView activeLinearView;
        /// <summary>
        /// Holds the active page.
        /// </summary>
        private ViewModel.Elements.Page activePage;
        /// <summary>
        /// Holds the value of the <see cref="Selection"/> property.
        /// </summary>
        private ReadOnlyObservableCollection<ViewModel.Elements.CWNotation.CoreSymbol> selection;

        #endregion
    }
}
